package munci.core;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Arrays;

public abstract class WordImpl implements Word {
	
	protected final Method method;
	private final Object[] attributes;
	
	public WordImpl(Object... attributes) {
		this.attributes = attributes;
		for (Method method : getClass().getMethods()) {
			if (method.getName().equals("call")) {
				this.method = method;
				return;
			}
		}
		throw new RuntimeException("No 'call' method");
	}
	
	public Class<?> returnType() {
		return method.getReturnType();
	}
	
	public Type genericReturnType() {
		return method.getGenericReturnType();
	}
	
	public Class<?>[] parameterTypes() {
		return method.getParameterTypes();
	}
	
	public boolean hasAttribute(Object attribute) {
		for (Object attr : attributes) {
			if (attr == attribute) return true;
		}
		return false;
	}
	
	public Object invoke(Object... args) {
		try {
			return method.invoke(this, args);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	@Override public String toString() {
		StringBuilder str = new StringBuilder();
		String sep = "";
		for (Type type : method.getGenericParameterTypes()) {
			str.append(sep).append(type);
			sep = ", ";
		}
		str.append(" -- ").append(genericReturnType());
		
		if (attributes.length > 0) {
			str.append(' ').append(Arrays.asList(attributes));
		}
		return str.toString();
	}
	
	public static abstract class V0 extends WordImpl implements Word.V0 {
		public V0(Object... attributes) {
			super(attributes);
		}
		
		@Override public Object invoke(Object... args) {
			call();
			return null;
		}
	}

	public static abstract class V1<P1> extends WordImpl implements Word.V1<P1> {
		public V1(Object... attributes) {
			super(attributes);
		}
		
		@SuppressWarnings("unchecked") @Override public Object invoke(Object... args) {
			call((P1) args[0]);
			return null;
		}
	}

	public static abstract class V2<P1, P2> extends WordImpl implements Word.V2<P1, P2> {
		public V2(Object... attributes) {
			super(attributes);
		}
		
		@SuppressWarnings("unchecked") @Override public Object invoke(Object... args) {
			call((P1) args[0], (P2) args[1]);
			return null;
		}
	}

	public static abstract class V3<P1, P2, P3> extends WordImpl implements Word.V3<P1, P2, P3> {
		public V3(Object... attributes) {
			super(attributes);
		}
		
		@SuppressWarnings("unchecked") @Override public Object invoke(Object... args) {
			call((P1) args[0], (P2) args[1], (P3) args[2]);
			return null;
		}
	}
	
	public static abstract class V4<P1, P2, P3, P4> extends WordImpl implements Word.V4<P1, P2, P3, P4> {
		public V4(Object... attributes) {
			super(attributes);
		}
		
		@SuppressWarnings("unchecked") @Override public Object invoke(Object... args) {
			call((P1) args[0], (P2) args[1], (P3) args[2], (P4) args[3]);
			return null;
		}
	}

	private static abstract class Returns<R> extends WordImpl {
		public Returns(Object... attributes) {
			super(attributes);
		}
		
		@SuppressWarnings("unchecked") public Class<R> returnType() {
			return (Class<R>) method.getReturnType();
		}
	}
	
	public static abstract class R0<R> extends Returns<R> implements Word.R0<R> {
		public R0(Object... attributes) {
			super(attributes);
		}
		
		@Override public R invoke(Object... args) {
			return call();
		}
	}
	
	public static abstract class R1<R, P1> extends Returns<R> implements Word.R1<R, P1> {
		public R1(Object... attributes) {
			super(attributes);
		}
		
		@SuppressWarnings("unchecked") @Override public R invoke(Object... args) {
			return call((P1) args[0]);
		}
	}

	public static abstract class R2<R, P1, P2> extends Returns<R> implements Word.R2<R, P1, P2> {
		public R2(Object... attributes) {
			super(attributes);
		}
		
		@SuppressWarnings("unchecked") @Override public R invoke(Object... args) {
			return call((P1) args[0], (P2) args[1]);
		}
	}

	public static abstract class R3<R, P1, P2, P3> extends Returns<R> implements Word.R3<R, P1, P2, P3> {
		public R3(Object... attributes) {
			super(attributes);
		}
		
		@SuppressWarnings("unchecked") @Override public R invoke(Object... args) {
			return call((P1) args[0], (P2) args[1], (P3) args[2]);
		}
	}

	public static abstract class R4<R, P1, P2, P3, P4> extends Returns<R> implements Word.R4<R, P1, P2, P3, P4> {
		public R4(Object... attributes) {
			super(attributes);
		}
		
		@SuppressWarnings("unchecked") @Override public R invoke(Object... args) {
			return call((P1) args[0], (P2) args[1], (P3) args[2], (P4) args[3]);
		}
	}
}
